Homebrew Handbook for SNES

Chapter 1: Initializing the SNES

In this chapter we’ll look at startup procedures and a few helper macros for loading tile data into VRAM. These samples were taken from the simple DEMOROM demo, available (with full X816 source) on the SNES Developer's Lobby homepage.

Section 1.1: Hardware Reset

Before anything can be done, the SNES must be initialized. While most emulators and the real hardware tend to start up with the same status every time, you should not assume that, for example, RAM will be cleared to zero. You must do it yourself. The SNES registers must also be setup to display a blank screen while you start up your game code. The procedure is fairly straightforward: Write zeros into most registers, with special values going in certain registers. Here’s a code snippit that can be used for most SNES games:

;============================================================================
; InitializeSNES -- Load palette data into PPU Color RAM
;----------------------------------------------------------------------------
; In: None
;----------------------------------------------------------------------------
; Out: None
;----------------------------------------------------------------------------
; Modifies: A, X, P
;----------------------------------------------------------------------------
InitializeSNES:
REP #$30
SEP #$20
.mem 8
.index 16
LDA #$8F
STA $2100
LDA #$80
STA $2115
LDA #00
LDX #$2101
CLRRegLoop:
STA $0000,X
INX
CPX #$210D
BNE CLRRegLoop
STA $210E
STA $210E
STA $210F
STA $210F
STA $2110
STA $2110
STA $2111
STA $2111
STA $2112
STA $2112
STA $2113
STA $2113
STA $2114
STA $2114
LDX #$2116
CLRRegLoopB:
STA $0000,X
INX
CPX #$2133
BNE CLRRegLoopB
LDX #$4200
CLRRegLoopC:
STA $0000,X
INX
CPX #$420D
BNE CLRRegLoopC
RTS
;============================================================================
 

As you can see, the init code does very little real work and accesses a lot of registers you probably don’t understand the purpose of. Following this reset, you don’t need to worry about them until you’re ready to use them. This subroutine, InitializeSNES, should be called immediately by your RESET handler, like this:

; Reset Vector Handler and the beginning of our ROM
.ORG $8000
sei ;disable interrupts
clc ;switch to native mode
xce
jsr InitializeSNES

At this point, we’re ready to get into game specific initialization code.

 

Section 1.2: Game Reset

One of the first things you should do is setup the graphics mode you want. For example, in my demo I used mode 1, which has two 16 color and a 4 color background:

rep #$30 ;8b mem, 16b X
sep #$20
.mem 8
.index 16
lda #$01 ;Set video mode 1
sta $2105

The next step is to load your tiles into VRAM. To do this, I wrote a helper macro called LoadBlockToVRAM.  It uses DMA channels to load a block of data into VRAM. It does not necessarily have to be tile data, but that was the intended purpose of the macro. It could also be used to load maps.  (See LOADVRAM.ASM for code)

Using this macro to load tiles is pretty simple:

lda #$01
sta $210B ;Set BG1's Tile VRAM offset to $1000
.LoadBlockToVRAM (TiledataBank,Tiledata,$1000,$6400) ;load a 25600 byte tileset from Tiledata into VRAM at $1000

Note that register $210B was set to $01; this tells the PPU to use VRAM address $1000 as the beginning of tile memory for BG1. Also note that VRAM addresses are word addreses rather than byte addresses. Thus, VRAM address $1000 and $4200 are actually 25600 bytes apart, not just the 12800 that you would expect if it were bytes. This "feature" of the SNES VRAM can sometimes make address calculations tricky. A side effect of that is that only $0000 to $7FFF are valid VRAM addresses.

Now that the tiles are in place, we’d better load the palette into the color registers. I have another helper macro for this, called LoadPalette.  Using this macro, you can load many color entries at once.

.LoadPalette (ColorDataBank, ColorData, FirstColorIndex, NumberOfColors)

Note that ColorData is a label marking a binary block of color data. In the tile load example above, Tiledata is also a label for binary blocks. These blocks are usually external .bin files generated by a tile editor. For example:

ColorData
.incbin "tilesclr.bin"
Tiledata
.incbin "tiles.bin"

How these includes work varies from assembler to assembler. All of these samples so far were tested using X816, but they are definately not compatable with SNIDE 0.4 or later. SNIDE has its own way of handling symbols and resources like tile and palette data.

Now we’re ready to actually display something on the screen. To do so, we note that BG1’s SC address is set to VRAM address $0000 (the default setting), so that is where we must load our map. In the sample, the tiles are segments of a BMP file in the order they were captured by SNIDE’s BMP to SNES tile converter. So, to display the picture we must set the map for BG1 to an incrementing list of tiles 0 to 1023:

stz $2116 ;reset VRAM pointer
stz $2117
ldx #$0000 ;init counter
- stx $2118 ;store the tile into VRAM
inx ;next tile
cpx #$0800 ;loop
bne –

This code snippit can be used to make a single unmoving image, perhaps a title screen. However, it does not make the most efficient use of memory, as tiles could be reused to avoid redundancy. Much more intricate code can be used to generate game fields, menus, text, etc. This is up to you. :) Later chapters will cover some of those techniques.

We’re not quite done with our demo: we’ve got to turn on the display and halt the CPU!

lda #$01 ;enable BG1 to display
sta $212C
lda #$0F ;turn on the screen, full brightness
sta $2100
- bra - ;begin an infinite loop to halt the program. We could also simply STP here.